//===============================================================================
// Microsoft Architecture Strategy Team
// LitwareHR - SaaS Sample Application
//===============================================================================
// Copyright  Microsoft Corporation.  All rights reserved.
// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY
// OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT
// LIMITED TO THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
// FITNESS FOR A PARTICULAR PURPOSE.
//===============================================================================
// The example companies, organizations, products, domain names,
// e-mail addresses, logos, people, places, and events depicted
// herein are fictitious.  No association with any real company,
// organization, product, domain name, email address, logo, person,
// places, or events is intended or should be inferred.
//===============================================================================



using System;
using System.Collections.Generic;
using System.Text;
using System.Workflow.Runtime;
using System.Workflow.Runtime.Hosting;
using System.Workflow.Runtime.Tracking;
using System.Workflow.Activities;
using System.Configuration;
using System.Data.Common;
using System.Data;
using System.Globalization;

using Shp.Runtime.Contract;

using LitwareHR.Recruiting.Contract;
using LitwareHR.Recruiting.Workflows;
using LitwareHR.Recruiting.Workflows.Activities;

using Shp.Runtime.Services;
using System.Collections.ObjectModel;
using SqlDataModel = LitwareHR.Recruiting.DataModel.Sql;
using SitkaDataModel = LitwareHR.Recruiting.DataModel.Sitka;


namespace LitwareHR.Recruiting.Services
{
    public static class ProcessLogic
    {
        private static SqlDataModel.RecruitingDataModel dataModelSql = new SqlDataModel.RecruitingDataModel();
        private static SitkaDataModel.RecruitingDataModel dataModelSitka = new SitkaDataModel.RecruitingDataModel();
        static private WorkflowRuntime runtime;

        public static WorkflowRuntime Runtime
        {
            get
            {
                if (runtime == null)
                {
                    runtime = new WorkflowRuntime("WorkflowRuntime");
                    if (runtime.GetService<ExternalDataExchangeService>() != null)
                    {
                        runtime.GetService<ExternalDataExchangeService>().AddService(new RecruitingMoniker());
                        runtime.GetService<ExternalDataExchangeService>().AddService(new EvaluationMoniker());
                    }
                    runtime.AddService(new WorkflowFactoryRuntimeService());
                    runtime.StartRuntime();
                }
                return runtime;
            }
        }

        public static Guid OpenRecruitingProcess(Position position)
        {
            Dictionary<string, object> parameters = new Dictionary<string, object>();
            parameters.Add("TenantId", position.TenantId);
            parameters.Add("PositionId", position.Id);
            parameters.Add("Position", position);
            WorkflowInstance workflowInstance = Runtime.CreateWorkflow(typeof(RecruitingProcess), parameters);
            workflowInstance.Start();
            RunWorkflowInScheduler(workflowInstance.InstanceId);

            return workflowInstance.InstanceId;
        }

        public static Collection<Guid> GetOpenPositionList(Guid tenantId)
        {
            SqlTrackingQuery sqlTrackingQuery = new SqlTrackingQuery(ConfigurationManager.ConnectionStrings["TrackingStore"].ConnectionString);
            SqlTrackingQueryOptions options = new SqlTrackingQueryOptions();
            options.WorkflowType = typeof(RecruitingProcess);
            options.WorkflowStatus = WorkflowStatus.Running;
            options.TrackingDataItems.Add(new TrackingDataItemValue("RecruitingProcess", "TenantId", tenantId.ToString()));

            IList<SqlTrackingWorkflowInstance> workflows = sqlTrackingQuery.GetWorkflows(options);
            Collection<Guid> list = new Collection<Guid>();
            foreach (SqlTrackingWorkflowInstance workflow in workflows)
            {
                Guid positionId = Guid.Empty;
                int activityEventsCount = workflow.ActivityEvents.Count;
                if (activityEventsCount > 0)
                {
                    ActivityTrackingRecord record = (ActivityTrackingRecord)workflow.ActivityEvents[activityEventsCount - 1];
                    foreach (TrackingDataItem dataItem in record.Body)
                    {
                        switch (dataItem.FieldName)
                        {
                            case "PositionId":
                                positionId = (Guid)dataItem.Data;
                                list.Add(positionId);
                                break;
                        }
                    }
                }
                
            }
            return list;
        }

        public static Collection<Position> GetOpenPositionEntityList(Guid tenantId)
        {
            if (tenantId == Guid.Empty)
                throw new ArgumentNullException("tenantId");

            Collection<Guid> positionList = ProcessLogic.GetOpenPositionList(tenantId);
            string tenantAlias = SystemLogic.GetTenantAlias(tenantId);
            List<Guid> positionListAux = new List<Guid>(positionList);
            
            StringPair[][] positions = DatamodelLogic.GetInstanceList(tenantAlias, "position", positionListAux.ToArray());

            Collection<Position> posList = new Collection<Position>();
            foreach (StringPair[] position in positions)
            {
                Guid id = new Guid();
                Collection<Field> fields = new Collection<Field>();
                foreach (StringPair field in position)
                {
                    if (field.Key.ToUpper(CultureInfo.InvariantCulture).Equals("ID"))
                    {
                        id = new Guid(field.Value);
                    }
                    else
                    {
                        fields.Add(new Field(field.Key, field.Value));
                    }
                }

                Position pos = new Position(id, fields);
                pos.TenantId = tenantId;

                posList.Add(pos);
            }

            return posList;
        }

        private static void RunWorkflowInScheduler(Guid instanceId)
        {
            ManualWorkflowSchedulerService scheduler = Runtime.GetService<ManualWorkflowSchedulerService>();
            scheduler.RunWorkflow(instanceId);
        }

        public static StringPair[][] GetRecruitingWorkflowListByApplicant(Guid tenantId, Upn upn)
        {
            return dataModelSql.GetRecruitingWorkflowListByApplicant(tenantId, upn);
        }

        public static StringPair[][] GetRecruitingWorkflowList(Guid tenantId)
        {
            return dataModelSql.GetRecruitingWorkflowList(tenantId);
        }

        public static Resume GetResume(Guid resumeId)
        {
            if (resumeId == Guid.Empty)
                throw new ArgumentNullException("resumeId");

            return dataModelSitka.GetResume(resumeId, Context.TenantId);
        }

        public static void SubmitResume(Resume resume, Upn upn)
        {
            //create resume
            resume.Upn = upn;

            dataModelSitka.SubmitResume(resume, Context.TenantId);

            //start workflow instance
            RecruitingMoniker service = Runtime.GetService<RecruitingMoniker>();
            Guid recruitingProcessId = GetRecruitingProcessByPositionId(resume.PositionId);
            service.SubmitResume(recruitingProcessId, resume, upn);
            RunWorkflowInScheduler(recruitingProcessId);
        }

        public static void AddVideoResume(Resume resume, Guid tenantId)
        {            
            //update the flag in the resume table
            bool videoUploaded = false;
            try
            {
                //upload video resume
                if (resume.VideoResume != null && resume.VideoResume.Length > 0)
                {
                    SilverlightAccount slAccount = GetSilverlightAccount(tenantId);

                    if (slAccount != null)
                    {
                        string waterMark = slAccount.TenantAlias;

                        //Get Position title & code
                        StringPair title = GetPositionField(slAccount.TenantAlias, resume.PositionId, "title");
                        if (title != null)
                        {
                            waterMark = String.Concat(waterMark, " <LineBreak /> ", title.Value);
                            StringPair code = GetPositionField(slAccount.TenantAlias, resume.PositionId, "code");
                            if (code != null)
                            {
                                waterMark = String.Concat(waterMark, "(", code.Value, ")");
                            }                        
                        }

                        if (SlsHelper.UploadFileSet(slAccount.AccountId.Trim(), slAccount.AccountKey.Trim(), resume.VideoResume, resume.Id.ToString().Replace("-", String.Empty), waterMark))
                        {
                            dataModelSitka.AddVideoResume(resume, Context.TenantId);
                            videoUploaded = true;
                        }
                    }
                }
                if (!videoUploaded)
                {
                    dataModelSitka.SetErrorOnVideoResume(resume, Context.TenantId);
                }
            }
            catch 
            {
                dataModelSitka.SetErrorOnVideoResume(resume, Context.TenantId);
                throw;
            }
        }

        private static StringPair GetPositionField(string tenantAlias, Guid positionId, string fieldName)
        {
            //get position details
            StringPair[][] positions = DatamodelLogic.GetInstanceList(tenantAlias, "position", new Guid[] { positionId });
            if (positions.GetLength(0) > 0)
            {
                List<StringPair> items = new List<StringPair>(positions[0]);
                StringPair field = items.Find(item=> item.Key.Equals(fieldName, StringComparison.OrdinalIgnoreCase));
                return field;
            }

            return null;
        }

        public static SilverlightAccount GetSilverlightAccount(Guid tenantId)
        {
            return dataModelSitka.GetSilverlightAccount(tenantId);
        }

        private static Guid GetRecruitingProcessByPositionId(Guid positionId)
        {
            SqlTrackingQuery sqlTrackingQuery = new SqlTrackingQuery(ConfigurationManager.ConnectionStrings["TrackingStore"].ConnectionString);
            SqlTrackingQueryOptions options = new SqlTrackingQueryOptions();
            options.WorkflowStatus = WorkflowStatus.Running;
            options.WorkflowType = typeof(RecruitingProcess);
            options.TrackingDataItems.Add(new TrackingDataItemValue("RecruitingProcess", "PositionId", positionId.ToString()));

            IList<SqlTrackingWorkflowInstance> workflows = sqlTrackingQuery.GetWorkflows(options);

            if (workflows.Count > 0)
                return workflows[workflows.Count - 1].WorkflowInstanceId;

            else
                return Guid.Empty;
        }

        public static void SubmitInterview(Interview interview, Upn upn)
        {
            EvaluationMoniker service = Runtime.GetService<EvaluationMoniker>();
            Guid evaluationProcessWorkflowId = interview.WorkflowId;
            service.SubmitInterview(evaluationProcessWorkflowId, interview, upn);
            RunWorkflowInScheduler(evaluationProcessWorkflowId);
        }

        public static void SubmitNegotiation(Negotiation negotiation, Upn upn)
        {
            EvaluationMoniker service = Runtime.GetService<EvaluationMoniker>();
            Guid evaluationProcessWorkflowId = negotiation.WorkflowId;
            service.SubmitNegotiation(evaluationProcessWorkflowId, negotiation, upn);
            RunWorkflowInScheduler(evaluationProcessWorkflowId);
        }

        public static void SubmitAcceptance(Acceptance acceptance, Upn upn)
        {
            EvaluationMoniker service = Runtime.GetService<EvaluationMoniker>();
            Guid evaluationProcessWorkflowId = acceptance.WorkflowId;
            service.SubmitAcceptance(evaluationProcessWorkflowId, acceptance, upn);
            RunWorkflowInScheduler(evaluationProcessWorkflowId);
        }

        public static void SubmitRejection(Rejection rejection, Upn upn)
        {
            EvaluationMoniker service = Runtime.GetService<EvaluationMoniker>();
            Guid evaluationProcessWorkflowId = rejection.WorkflowId;
            service.SubmitRejection(evaluationProcessWorkflowId, rejection, upn);
            RunWorkflowInScheduler(evaluationProcessWorkflowId);
        }

        public static Resume[] SearchResumes(PropertyFilter[] filters, Guid tenantId)
        {
            return dataModelSitka.SearchResumes(filters, tenantId);
        }

        public static Resume[] SearchCrossTenantResumes(PropertyFilter[] filters, Guid tenantId)
        {
            return dataModelSitka.SearchCrossTenantResumes(filters, tenantId);
        }
    }
}
